package com.vision.controller;

import com.vision.config.Constants;
import com.vision.config.WebSocketServer;
import com.vision.pojo.vo.*;
import com.vision.pojo.entity.User;
import com.vision.pojo.AjaxResult;
import com.vision.service.UserService;
import com.vision.utils.JwtUtil;
import com.vision.utils.Md5Util;
import com.vision.utils.ThreadLocalUtil;
import jakarta.servlet.http.HttpSession;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Pattern;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("/user")
public class UserController {

    @Autowired
    private UserService userService;
    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    /**
     * 向邮箱或手机号发送验证码
     * @param emailOrPhone 邮箱或者手机号
     * @return 验证码
     */
    @PostMapping("/sendVerificationCode")
    public AjaxResult<String> sendVerificationCode(@Pattern(regexp="(^1[3456789]\\d{9}$)|(^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$)") String emailOrPhone) throws Exception {
        return AjaxResult.success(userService.sendVerificationCode(emailOrPhone), "验证码发送成功");
    }

    /**
     * 注册
     * @param emailOrPhone 用户名
     * @return 注册的用户信息
     */
    @PostMapping("/register")
    public AjaxResult<String> register(@Pattern(regexp="(^1[3456789]\\d{9}$)|(^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$)") String emailOrPhone, @Pattern(regexp="^\\d{6}$") String verificationCode) {
        // 判断用户是否存在
        if (userService.queryUserByPhoneOrEmail(emailOrPhone) != null) {
            return AjaxResult.error("该用户已存在");
        }
        // 判断验证码是否正确
        if (verifyVerificationAndRemove(emailOrPhone, verificationCode)) {
            return AjaxResult.error("验证码错误");
        }
        // 注册
        User user = userService.register(emailOrPhone);
        return AjaxResult.success(generateTokenAndCache(user),"登录成功");
    }

    /**
     * 登录 通过验证码
     * @param emailOrPhone 邮箱或手机号
     * @param verificationCode 验证码
     * @return token
     */
    @PostMapping("/loginByVerificationCode")
    public AjaxResult<String> loginByVerificationCode (@Pattern(regexp="(^1[3456789]\\d{9}$)|(^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$)") String emailOrPhone, @Pattern(regexp="^\\d{6}$") String verificationCode){
        // 判断验证码是否正确
        if (verifyVerificationAndRemove(emailOrPhone, verificationCode)) {
            return AjaxResult.error("验证码错误");
        }

        // 获取当前用户信息
        User user = userService.queryUserByPhoneOrEmail(emailOrPhone);
        // 判断用户是否存在
        if ( user == null ){
            return AjaxResult.error("该用户不存在");
        }
        return AjaxResult.success(generateTokenAndCache(user),"登录成功");
    }

    /**
     * 登录 通过密码
     * @param emailOrPhone 邮箱或手机号
     * @param password 密码
     * @return token
     */
    @PostMapping("/loginByPassword")
    public AjaxResult<String> loginByPassword(@Pattern(regexp="(^1[3456789]\\d{9}$)|(^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$)")String emailOrPhone,@Pattern(regexp="^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$")String password) {
        // 获取当前用户信息
        User user = userService.queryUserByPhoneOrEmail(emailOrPhone);
        // 判断用户是否存在
        if (user == null) {
            return AjaxResult.error("该用户不存在");
        }
        // 对密码进行 md5 加密 并 判断密码是否正确
        String userPassword = user.getPassword();
        if (!Md5Util.checkPassword(password, userPassword)) {
            return AjaxResult.error("密码错误");
        }
        return AjaxResult.success(generateTokenAndCache(user),"登录成功");
    }

    /**
     * 退出登录
     * @return 成功
     */
    @PostMapping("/logout")
    public AjaxResult<String> logoutController(HttpSession session) {
        logout(session);
        // 4. 返回成功
        return AjaxResult.success("退出成功");

    }

    /**
     * 更新用户基本信息
     * @param avatar 头像
     * @param nickname 昵称
     * @param gender 性别
     * @param description 描述
     * @return 成功
     */
    @PutMapping("/updateUserBaseInfo")
    public AjaxResult updateUserBaseInfo (String avatar, String nickname, Character gender, String description) {
        // 1. 封装用户数据
        User user = new User();
        user.setAvatar(avatar);
        user.setNickname(nickname);
        user.setGender(gender);
        user.setDescription(description);
        // 2. 更新用户信息
        userService.updateUserBaseInfo(user);
        return AjaxResult.success();
    }

    /**
     * 更新用户邮箱
     * @param email 邮箱
     * @return 成功
     */
    @PatchMapping("/updateEmail")
    public AjaxResult updateEmail (@Email String email, @Pattern(regexp="^\\d{6}$")String verificationCode) {
        // 1. 判断验证码是否正确
        if (verifyVerificationAndRemove(email, verificationCode)) {
            return AjaxResult.error("验证码错误");
        }
        // 2. 更新用户邮箱
        userService.updateEmail(email);
        return AjaxResult.success();
    }

    /**
     * 更新用户手机号
     * @param phone 手机号
     * @return 成功
     */
    @PatchMapping("/updatePhone")
    public AjaxResult updatePhone (@Pattern(regexp="^1[3456789]\\d{9}$") String phone, @Pattern(regexp="^\\d{6}$")String verificationCode) {
        // 1. 判断验证码是否正确
        if (verifyVerificationAndRemove(phone, verificationCode)) {
            return AjaxResult.error("验证码错误");
        }
        // 2. 更新用户手机号
        userService.UpdatePhone(phone);
        return AjaxResult.success();
    }

    /**
     * 更新用户密码
     * @param oldPassword 旧密码
     * @param password 新密码
     * @param rePassword 确认密码
     * @return 成功
     */
    @PutMapping("/updatePassword")
    public AjaxResult updatePassword (
            @Pattern(regexp="^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$") String oldPassword,
            @Pattern(regexp="^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$") String password,
            @Pattern(regexp="^(?=.*[A-Za-z])(?=.*\\d)[A-Za-z\\d]{8,}$") String rePassword,
            HttpSession session
    ){
        // 1. 判断旧密码与新密码是否相同
        if (oldPassword.equals(password)) {
            return AjaxResult.error("新密码不能与旧密码相同");
        }

        // 2. 判断旧密码是否与当前密码一致
        // 2.1 获取当前用户 id
        Map<String, Object> userInfo = ThreadLocalUtil.get();
        Integer id = (Integer) userInfo.get("id");
        // 2.2 查询当前用户信息
        User user = userService.getUserById(id);
        // 2.3 判断密码是否存在
        if (user.getPassword() != null) {
            // 2.4 判断旧密码是否正确
            if (!Md5Util.checkPassword(oldPassword, user.getPassword())) {
                return AjaxResult.error("旧密码错误");
            }
        }

        // 3. 判断新密码与确认密码是否相同
        if (!password.equals(rePassword)) {
            return AjaxResult.error("新密码与确认密码不相同");
        }

        // 4. 更新密码
        userService.updatePassword(id,password);

        logout(session); // 退出登录
        return AjaxResult.success("密码修改成功，请重新登录");
    }

    /**
     * 获取当前登录用户信息
     * @return 用户信息
     */
    @GetMapping("/getCurrentLoginUserInfo")
    public AjaxResult<CurrentLoginUserVO> getCurrentLoginUser() {
        return AjaxResult.success(userService.getCurrentLoginUser());
    }

    /**
     * 获取用户详细信息
     * @param userId 用户id
     * @return 用户详细信息
     */
    @GetMapping("/getUserDetailInfoById")
    public AjaxResult<UserInfoVO> getUserDetailInfoById (@NotNull Integer userId) {
        return AjaxResult.success(userService.getUserDetailInfoById(userId));
    }

    /**
     * 获取单个聊天用户
     * @param userId 目标对象聊天 id
     * @return 单个聊天用户
     */
    @GetMapping("/getUserInfo")
    public AjaxResult<User> getUserInfo (@NotNull Integer userId) {
        return AjaxResult.success(userService.getUserById(userId));
    }

    /**
     * 获取用户列表
     * @param pageNum 页码
     * @param pageSize 每页数量
     * @param nickname 昵称
     * @return 用户列表
     */
    @GetMapping("/getUserList")
    public AjaxResult<PageVO<UserInfoVO>> getUserList (
            @NotNull Integer pageNum,
            @NotNull Integer pageSize,
            @RequestParam(required = false) String nickname,
            HttpSession session) {
        // 1. 获取当前用户信息
        Map<String, Integer> claims = (Map<String, Integer>) session.getAttribute("claims");
        Integer userId = null;
        if (claims != null) {
            userId = claims.get("id");
        }
        // 2. 获取用户列表
        return AjaxResult.success(userService.getUserList(pageNum, pageSize, nickname,userId));
    }

    /**
     * 生成 token 并将 token 存入 redis
     * @param user 用户
     * @return token
     */
    private String generateTokenAndCache(User user) {

        // 1. 判断 redis 中是否存在该用户的 token
        if (stringRedisTemplate.opsForValue().get(user.getUsername()) != null) {
            // 1. 如果存在，则说明该用户已经登录，需要将之前的 token 删除
            stringRedisTemplate.delete(user.getUsername());
            // 2. 判断用户是否在线
            if (WebSocketServer.onLineUsers.get(user.getUsername()) != null) {
                // 2. 通过 websocket 通知用户
                MessageVO messageVO = new MessageVO();
                messageVO.setContent("您的账号在其他地方登录，您被迫下线");
                messageVO.setIsSystem(1);
                messageVO.setReceiverUsername(user.getUsername());
                messageVO.setState(Constants.WEBSOCKET_OFFLINE_NOTIFICATION_CODE);
                WebSocketServer.sendMessage(messageVO);
            }
        }

        // 2. 生成 token
        Map<String,Object> claims = new HashMap<String, Object>();
        claims.put("id", user.getId());
        claims.put("username", user.getUsername());
        String token = JwtUtil.genToken(claims);

        // 3. 将 token 存入 redis
        stringRedisTemplate.opsForValue().set(user.getUsername(), token, Constants.TOKEN_EXPIRATION_TIME, TimeUnit.MILLISECONDS);
        return token;
    }

    /**
     * 验证验证码并移除
     * @param emailOrPhone 邮箱或手机号
     * @param verificationCode 验证码
     * @return 返回 true 表示验证不通过，返回 false 表示验证通过
     */
    private boolean verifyVerificationAndRemove(String emailOrPhone, String verificationCode) {
        // 获取验证码
        String realVerificationCode = stringRedisTemplate.opsForValue().get(emailOrPhone);
        // 判断验证码是否正确
        if (!verificationCode.equals(realVerificationCode)) {
            return true;
        }
        // 移除验证码
        stringRedisTemplate.delete(emailOrPhone);
        return false;
    }

    /**
     * 退出当前用户登录
     * @param session 会话
     */
    private void logout (HttpSession session) {
        // 1. 清除用户信息
        Map<String,Object> claims = ThreadLocalUtil.get();
        if (claims != null) {
            String username = (String) claims.get("username");
            // 2. 删除 redis 中的 token
            stringRedisTemplate.delete(username);
        }
        // 4.2 清除 session
        session.removeAttribute("claims");
    }

}
